﻿
Public Class MetisCore

    Public Const MAX_NAME_LENGTH As Integer = 64
    Public Const MAX_DB_NAME_LENGTH As Integer = 30

    'Private WithEvents _Timer As New Timer()
    Private _RefreshInterval As Integer
    Private WithEvents _OraCon As New OracleConnection()
    Private _CurTempID As Integer = -1
    Private _CurTempFieldID As Integer = -1

    Private _LastFunctionListUpdate As Date = Date.MinValue

    Private WithEvents _DBTables As DB.DBTableList
    Private WithEvents _Dimensions As Dimensions.MetisDimensionList
    Private WithEvents _Namespaces As Functions.MetisNamespaceList
    Private WithEvents _Functions As Functions.MetisFunctionList
    Private WithEvents _Flows As Flows.MetisFlowList



    ''' <summary>Raised when the state of the connection to the Oracle-DB has changed</summary>
    ''' <param name="sender"></param>
    ''' <param name="e">Information of the current and the previous connection-state</param>
    ''' <remarks>Passed from Oracle-<see cref="Connection"/></remarks>
    Public Event ConnectionStateChange(ByVal sender As Object, ByVal e As System.Data.StateChangeEventArgs)


    Public Shared Function IsValidFieldName(ByVal FieldName As String, ByVal MaxLength As Integer) As Boolean
        If FieldName.Length > 0 AndAlso FieldName.Length <= MaxLength Then
            If Char.IsLetter(FieldName, 0) Or FieldName(0) = "_" Then
                For i As Integer = 0 To FieldName.Length - 1
                    If Not (Char.IsLetterOrDigit(FieldName, i) Or FieldName(i) = "_") Then
                        Return False
                    End If
                Next
            Else
                Return False
            End If
        Else
            Return False
        End If
        Return True
    End Function

    ''' <summary>Instanciates a new Object</summary>
    Sub New()

    End Sub

    ''' <summary>Instanciates a new Object and connects to the DB</summary>
    ''' <param name="SID">Oracle Service-ID as stated in tnsnames.ora</param>
    ''' <param name="Username">The login name of the Oracle-User</param>
    ''' <param name="Password">The passwort of the Oracle-User</param>
    ''' <param name="RefreshInterval">Interval in ms the repository is updated from the DB (default 60 sec)</param>
    Sub New(ByVal SID As String, ByVal Username As String, ByVal Password As String, Optional ByVal RefreshInterval As Integer = 60 * 1000)
        Connect(SID, Username, Password, RefreshInterval)
    End Sub

    ''' <summary>Connects to the DB</summary>
    ''' <param name="SID">Oracle Service-ID as stated in tnsnames.ora</param>
    ''' <param name="Username">The login name of the Oracle-User</param>
    ''' <param name="Password">The passwort of the Oracle-User</param>
    ''' <remarks>The connection will be closed if it is open.</remarks>
    Public Sub Connect(ByVal SID As String, ByVal Username As String, ByVal Password As String, Optional ByVal RefreshInterval As Integer = 60 * 1000)
        If _OraCon IsNot Nothing Then
            If _OraCon.State = Data.ConnectionState.Open Then _OraCon.Close()
        Else
            _OraCon = New OracleConnection()
        End If
        _OraCon.ConnectionString = "Data Source=" & SID & ";User ID=" & Username & ";Password=" & Password
        _RefreshInterval = RefreshInterval
        _OraCon.Open()
    End Sub

    ''' <summary>The DB-Connection</summary>
    Public ReadOnly Property Connection() As OracleConnection
        Get
            Return _OraCon
        End Get
    End Property

    Private Sub HandleConnectionStateChange(ByVal sender As Object, ByVal e As System.Data.StateChangeEventArgs) Handles _OraCon.StateChange
        If e.CurrentState = Data.ConnectionState.Closed OrElse e.CurrentState = Data.ConnectionState.Broken Then
            '_Timer.Enabled = False
            _DBTables = Nothing
            _Dimensions = Nothing
            _Flows = Nothing
            _Functions = Nothing
            _Namespaces = Nothing
        ElseIf e.CurrentState = Data.ConnectionState.Open AndAlso e.OriginalState = Data.ConnectionState.Closed Then
            ReloadDBTables()
            ReloadNamespaces()
            ReloadDimensions()
            ReloadFunctions()
            ReloadFlows()
            '_Timer.Enabled = True
            '_Timer.Interval = _RefreshInterval
        End If
        RaiseEvent ConnectionStateChange(sender, e)
    End Sub


    'Private Sub HandleTimer(ByVal sender As Object, ByVal e As System.EventArgs) Handles _Timer.Tick
    '    If _OraCon.State = Data.ConnectionState.Open Then
    '        ReloadDBTables()
    '        ReloadNamespaces()
    '        ReloadDimensions()
    '        ReloadFunctions()
    '        ReloadFlows()
    '    End If
    'End Sub

    ''' <summary>Generates a ID for new functions, flows and dimensions that will be used as PK in the DB</summary>
    ''' <returns>A positive integer generated by an Oracle-Sequence</returns>
    Public Function GetNextID() As Integer
        Dim OraCmd As OracleCommand = New OracleCommand("declare RET INT; BEGIN SELECT DAM_ID.NEXTVAL INTO :RET FROM dual; END;", _OraCon)
        Dim OraParam As OracleParameter = OraCmd.Parameters.Add("RET", OracleType.Int32)
        OraParam.Direction = Data.ParameterDirection.ReturnValue
        OraCmd.ExecuteScalar()
        Dim tmpVal As Object = OraCmd.Parameters(0).Value
        Return DirectCast(tmpVal, Integer)
    End Function

    ''' <summary>Generates a ID for new fields that will be used as PK in the DB</summary>
    ''' <returns>A positive integer generated by an Oracle-Sequence</returns>
    Public Function GetNextFieldID() As Integer
        Dim OraCmd As OracleCommand = New OracleCommand("declare RET INT; BEGIN SELECT DAM_FIELD_ID.NEXTVAL INTO :RET FROM dual; END;", _OraCon)
        Dim OraParam As OracleParameter = OraCmd.Parameters.Add("RET", OracleType.Int32)
        OraParam.Direction = Data.ParameterDirection.ReturnValue
        OraCmd.ExecuteScalar()
        Dim tmpVal As Object = OraCmd.Parameters(0).Value
        Return DirectCast(tmpVal, Integer)
    End Function

    ''' <summary>Generates a temporary ID for new but not yet saved dimensions, functions, etc. </summary>
    ''' <returns>A negative integer less than -1</returns>
    ''' <remarks></remarks>
    Public Function GetTemporaryID() As Integer
        _CurTempID -= 1
        Return _CurTempID
    End Function

    ''' <summary>Generates a temporary ID for new but not yet saved fields</summary>
    ''' <returns>A negative integer less than -1</returns>
    ''' <remarks></remarks>
    Public Function GetTemporaryFieldID() As Integer
        _CurTempFieldID -= 1
        Return _CurTempFieldID
    End Function

    ''' <summary>Updates the list of <see cref="DBTables">DB tables</see></summary>
    Public Sub ReloadDBTables()
        _DBTables = New DB.DBTableList(Me)
    End Sub

    ''' <summary>Updates the items of <see cref="Dimensions">Dimensions</see> that changed since last call.</summary>
    Public Sub ReloadDimensions()
        If _Dimensions Is Nothing Then
            _Dimensions = New Dimensions.MetisDimensionList(Me)
        Else
            _Dimensions.Reload()
        End If
    End Sub

    ''' <summary>Updates the items of <see cref="Namespaces">Namespaces</see> that changed since last call.</summary>
    Public Sub ReloadNamespaces()
        If _Namespaces Is Nothing Then
            _Namespaces = New Functions.MetisNamespaceList(Me)
        Else
            _Namespaces.Reload()
        End If
    End Sub

    ''' <summary>Updates the items of <see cref="Functions">Functions</see> that changed since last call.</summary>
    Public Sub ReloadFunctions()
        If _Functions Is Nothing Then
            _Functions = New Functions.MetisFunctionList(Me, True)
        Else
            _Functions.Reload()
        End If
    End Sub

    ''' <summary>Updates the items of <see cref="Flows">Flows</see> that changed since last call.</summary>
    Public Sub ReloadFlows()
        If _Flows Is Nothing Then
            _Flows = New Flows.MetisFlowList(Me)
        Else
            _Flows.Reload()
        End If
    End Sub

    ''' <summary>List of DB Tables</summary>
    ''' <remarks>Will be loaded on first access</remarks>
    Public ReadOnly Property DBTables() As DB.DBTableList
        Get
            If _DBTables Is Nothing AndAlso _OraCon.State = ConnectionState.Open Then ReloadDBTables()
            Return _DBTables
        End Get
    End Property

    ''' <summary>List of Metis Dimensions loaded from DB</summary>
    ''' <remarks>Will be loaded on first access</remarks>
    Public ReadOnly Property Dimensions() As Dimensions.MetisDimensionList
        Get
            If _Dimensions Is Nothing AndAlso _OraCon.State = ConnectionState.Open Then ReloadDimensions()
            Return _Dimensions
        End Get
    End Property

    ''' <summary>List of Metis Namespaces loaded from DB</summary>
    ''' <remarks>Will be loaded on first access</remarks>
    Public ReadOnly Property Namespaces() As Functions.MetisNamespaceList
        Get
            If _Namespaces Is Nothing AndAlso _OraCon.State = ConnectionState.Open Then ReloadNamespaces()
            Return _Namespaces
        End Get
    End Property

    ''' <summary>List of Metis Functions loaded from DB</summary>
    ''' <remarks>Will be loaded on first access</remarks>
    Public ReadOnly Property Functions() As Functions.MetisFunctionList
        Get
            If _Functions Is Nothing AndAlso _OraCon.State = ConnectionState.Open Then ReloadFunctions()
            Return _Functions
        End Get
    End Property

    ''' <summary>List of Metis Flows loaded from DB</summary>
    ''' <remarks>Will be loaded on first access</remarks>
    Public ReadOnly Property Flows() As Flows.MetisFlowList
        Get
            If _Flows Is Nothing AndAlso _OraCon.State = ConnectionState.Open Then ReloadFlows()
            Return _Flows
        End Get
    End Property

End Class
